ReactのMain Concept:10. Lifting State Up
問題:複数のcomponentが、同じデータへの変更を反映させたい場合どうすればよいか?
換言すると、「複数のcomponentがstateを共有したい場合にどうすればいいか?」
結論:共通のstateを、最も近い共通の祖先にliftingするのがおすすめ
この節では、これをどうやるのか、「与えられた温度において水が沸騰するかどうかを計算する温度計」を例に説明する
だらだら訳してしまったが、コードみたらすぐわかるkadoyau.icon
Adding a Second Input
摂氏だけでなくて華氏もつけたくなった。当然それらの入力は同期してほしい
scaleパラメータを渡すことで切り替えられる摂氏/華氏2つのフォームを生成した
課題
2つの値が同期しない
沸騰を判定する表示(BoilingVerdict)が表示できない
BolingVerdictが表示できない
Calculratorが利用しているTemperatureImputが温度を管理しているので、Calculatorは現在温度を知らない
Writing Conversion Functions
摂氏と華氏を相互変換する関数を書く
tryConvert(temperature, convert:(float)=>(float))
Lifting State Up
摂氏と華氏のTemperatureInputcomponentはどちらもlocal stateを独立して持っている
これらを同期したい。もしユーザが摂氏に入力したら、華氏も変わってほしい
Reactで共通のstateをもたせるためには、そのstateを必要とするcomponentの、最も近い共通の先祖にそのstateをもたせる
TempretureInputからlocal stateを取り除いて、代わりにCalculatorに移す
もしCalculatorがshared stateを持っていたら、それは両方の入力における現在温度の"source of truth"になる
Calculatorは、常に同期された一貫性のある値を、両方のTemperatureInputcomponentに渡すことができる
どのように動くかstep by stepでみていこう
温度の表示
TemperatureInputの表示する温度は外から渡されるので、表示部はthis.props.temperatureになる
温度の変更
Reactでは、componentを"controlled"にすることでこの問題を解決できる
DOM<input>がvalueとonChangepropsの両方を受け取ることができるように、TemperatureInputもtempratureとonTempretureChangepropsを親のCalculatorから受け取ることができる
onTempretureChangeには親が自分のlocal stateを変更する関数を渡す
両方のinputを新しい値で書き換えれる
Calcelatorはinputから"lifted up"してきた現在のtempratertureとscaleを受け取りstateに保存する
両方のinputに必要な最小限のデータがこの2つ
華氏と摂氏の療法を保存するのは無駄。最新の入力値のみ保存すれば良い
受け取った値を元にもう一方を計算する
Recap
ReactはDOMの<input>においてonChangeで指定されたfunctionをcallする。今回の場合、TemperatureInputコンポーネントにおけるhandleChangeだった
TemperatureInput componentのhandleChangeメソッドはthis.props.onTemperatureChange()をあらたな所望の値で呼び出す。onTemperatureChange()のpropsはその親コンポーネントであるCalculatorから提供されている
前回レンダリング時に、Calculatorは以下のことを決定している
摂氏のTemperatureInputのonTemperatureChangeはCalculatorのhandleCelsiusChangeメソッド
華氏のTemperatureInputのonTemperatureChangeはCalculatorの handleFahrenheitChangeメソッド
このため、それら2つのうちいずれかのCalculatorメソッドは、どちらの入力値が編集されたかに依存して呼ばれる
それらのメソッド(handle~)の中で、Calculator componentはReactに再描画を依頼する
自身のthis.setState()を、新しい入力値およびたった今編集したscaleの入力値とともに呼び出してもらう
ReactはCalculator componentのrender()を呼んでUIがどうあるべきか把握する。両方の入力値は現在の温度と有効なscaleから計算される。温度の変換はここで実行される
Reactは各temperatureImput comopnentのrender()を、Calculatorによって与えられた新しいpropsとともにcallする。すると、ReactはUIがどのようになるかわかる
BoiligVerdict componentのrender()を呼び、prosとして摂氏の温度を渡す
React DOMはboiling verdictでDOMを更新し、DOMは所望の入力値と一致させる
編集したinputは現在の値を受け取り、他のinputは変換後の値で更新される
Lessons Learned
Reactアプリケーションにおいて変更されたいかなるデータに対しても、単一の"sorce of truth"がなければならない
ふつう、stateはレンダリングが必要なコンポーネントにまず追加され、それから他のstateを必要としているコンポーネントに追加される。あなたはそれらの最も近い共通の祖先にstateをliftingすることができる。
異なるコンポーネント間でのstateをsyncすることを試みる代わりに、top-down data flowに頼るべき
Lifting stateは双方向バインディングのアプローチよりもボイラープレートをたくさん書かないといけなくなるという欠点がある
しかし、バグを見つけ出して隔離するのは楽になる。
いかなるstateもあるcomponentに住んでいて、そのcomponentだけがそのstateを変更できるので、バグの存在できる範囲はかなり減る
加えて、ユーザの入力を排除したり変換したりするロジックを実装することができる
もし何かの値をpropsかstateのどちらかで得る選択肢があるなら、stateには持つべきではない(propsで渡そう)
例えば
x 華氏と摂氏の両方をもつ
o 最後に編集された温度とスケール(華氏or摂氏)を持つ。他の値はrender()でそれらから算出する
この方法によって、ユーザ入力の精度を失うことなく、他のフィールドにあうように値を丸めるのが明快になる
UIのバグを見つけたら